using System;
using Microsoft.SPOT;
using OST.Shared.Interfaces;
using OST.Shared.Extensions;

namespace OST.Shared
{
    /// <summary>
    /// event handler (delegate) for stopwatch start executed
    /// </summary>
    /// <param name="timestamp">@this time-point</param>
    public delegate void delStartExecuted(DateTime timestamp);
    /// <summary>
    /// event handler (delegate) for stopwatch stop executed
    /// </summary>
    /// <param name="timestamp">@this time-point</param>
    public delegate void delStopExecuted(DateTime timestamp);
    /// <summary>
    /// event handler (delegate) for stopwatch reset executed
    /// </summary>
    /// <param name="timestamp">@this time-point</param>
    public delegate void delResetExecuted(DateTime timestamp);

    /// <summary>
    /// stopwatch implementation, based on 'start(-timestamp) - stop(-timestamp) = duration'
    /// </summary>
    public class Stopwatch : INotifyPropertyChanged
    {
        #region Fields
        DateTime _dtLastStart;
        TimeSpan _tsCurrentRuntime;

        #endregion

        #region CTOR

        public Stopwatch()
        {
            Reset();
        }
        #endregion

        #region Methods

        public void Start()
        {
            IsRunning = true;
            _dtLastStart = DateTime.Now;

            OnStartExecuted(_dtLastStart);
        }

        public void Stop()
        {
            if (_dtLastStart == DateTime.MinValue)
            {
                return;
            }
            var ts = DateTime.Now;
            LastRuntime = CalcRuntime(ts);
            IsRunning = false;

            OnStopExecuted(ts);
        }

        public void Reset()
        {
            _dtLastStart = DateTime.MinValue;
            LastRuntime = new TimeSpan(0, 0, 0, 0, 0);

            OnResetExecuted(DateTime.Now);
        }

        private TimeSpan CalcRuntime(DateTime timestamp)
        {
            return LastRuntime + (timestamp - _dtLastStart);
        }

        public static int ConvertTimeSpan2ElapsedMS(TimeSpan ts)
        {
            int elapsedMilliseconds = 0;

            elapsedMilliseconds += ts.Milliseconds;
            elapsedMilliseconds += (ts.Seconds > 0) ? ts.Seconds * 1000 : 0;
            elapsedMilliseconds += (ts.Minutes > 0) ? ts.Minutes * 60000 : 0;
            elapsedMilliseconds += (ts.Hours > 0) ? ts.Hours * 3600000 : 0;
            elapsedMilliseconds += (ts.Days > 0) ? ts.Days * 86400000 : 0;

            return elapsedMilliseconds;
        }
        #endregion

        #region Properties
        public TimeSpan LastRuntime
        {
            get
            {
                return _tsCurrentRuntime;
            }
            set
            {
                _tsCurrentRuntime = value;
                OnNotifyPropertyChanged("Runtime", _tsCurrentRuntime);
            }
        }

        public DateTime LastStartTimeStamp
        {
            get 
            { 
                return _dtLastStart;
            }
        }

        public int RuntimeInMilliseconds
        {
            get
            {
                return ConvertTimeSpan2ElapsedMS(LastRuntime);
            }
        }

        public bool IsRunning { get; private set; }
        #endregion

        #region Events

        public event delStartExecuted StartExecuted;
        protected void OnStartExecuted(DateTime timestamp)
        {
            if (StartExecuted != null)
            {
                StartExecuted(timestamp);
            }
        }

        public event delStopExecuted StopExecuted;
        protected void OnStopExecuted(DateTime timestamp)
        {
            if (StopExecuted != null)
            {
                StopExecuted(timestamp);
            }
        }


        public event delResetExecuted ResetExecuted;
        protected void OnResetExecuted(DateTime timestamp)
        {
            if (ResetExecuted != null)
            {
                ResetExecuted(timestamp);
            }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// notifies clients ove changes in puplic properties
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnNotifyPropertyChanged(String propertyName, object newValue)
        {
            if (PropertyChanged != null && !propertyName.IsNullOrEmpty())
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName, newValue));
            }
        }

        #endregion
    }
}
